package com.chinaisafe.user.service.impl;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.alibaba.nacos.shaded.com.google.common.collect.Lists;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.chinaisafe.base.common.dto.auth.AuthUser;
import com.chinaisafe.base.common.enums.AnswerCodeEnum;
import com.chinaisafe.base.common.enums.UserTypeEnum;
import com.chinaisafe.base.common.util.AssertUtils;
import com.chinaisafe.base.common.vo.Answer;
import com.chinaisafe.base.redis.util.RedisUtil;
import com.chinaisafe.common.sdk.constant.UserConstant;
import com.chinaisafe.common.sdk.enums.ExceptionCodeEnum;
import com.chinaisafe.gateway.client.auth.GateWayUserClient;
import com.chinaisafe.gateway.client.auth.UaaAuthHelper;
import com.chinaisafe.gateway.client.config.properties.ChinaisafeAuthorizationClientProperties;
import com.chinaisafe.gateway.sdk.enums.ClientTypeEnum;
import com.chinaisafe.gateway.sdk.vo.request.GateWayGetTokenReqVo;
import com.chinaisafe.gateway.sdk.vo.request.GateWayLoginOutReqVo;
import com.chinaisafe.gateway.sdk.vo.request.GatewayChangeTokenReqVo;
import com.chinaisafe.gateway.sdk.vo.request.UserCheckSourceReqVo;
import com.chinaisafe.user.entity.SysAccount;
import com.chinaisafe.user.entity.SysDept;
import com.chinaisafe.user.entity.SysTenant;
import com.chinaisafe.user.entity.SysUser;
import com.chinaisafe.user.mapper.SysAccountMapper;
import com.chinaisafe.user.mapper.SysDeptMapper;
import com.chinaisafe.user.mapper.SysTenantMapper;
import com.chinaisafe.user.mapper.SysUserMapper;
import com.chinaisafe.user.service.UserLoginService;
import com.chinaisafe.user.vo.request.*;
import com.chinaisafe.user.vo.response.LoginRespVO;
import com.chinaisafe.user.vo.response.UserInfoRespVO;
import com.wf.captcha.SpecCaptcha;
import com.wf.captcha.base.Captcha;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Objects;
import java.util.Optional;

/**
 * UserLoginServiceImpl
 * Description：
 *
 * @author xuefu.shu
 * @date 2024-07-12 16:34
 */
@Slf4j
@Service
public class UserLoginServiceImpl implements UserLoginService {
    @Value("${spring.profiles.active:dev}")
    private String application;

    @Resource
    private ChinaisafeAuthorizationClientProperties chinaisafeAuthorizationClientProperties;

    @Resource
    private SysTenantMapper sysTenantMapper;

    @Resource
    private SysAccountMapper accountMapper;

    @Resource
    private SysDeptMapper sysDeptMapper;

    @Resource
    private SysUserMapper sysUserMapper;

    @Resource
    private GateWayUserClient gateWayUserClient;

    /**
     * 测试环境验证码
     */
    private static final String VER_CODE = "123456";

    /**
     * 测试环境
     */
    private static final List<String> TEST_ENVIRONMENT = Lists.newArrayList("dev", "test");


    @Override
    public String captcha(UserCaptChaReqVO captChaReqVO) {
        AssertUtils.isNotBank(captChaReqVO.getMobile(), "手机号不能为空");

        SpecCaptcha specCaptcha = new SpecCaptcha(130, 48, 5);
        try {
            specCaptcha.setFont(Captcha.FONT_2);
        } catch (Exception e) {
            log.error("字体加载失败", e);
        }
        String verCode = specCaptcha.text().toLowerCase();
        String base64 = specCaptcha.toBase64();

        // 验证码缓存起来
        String key = StrUtil.format(UserConstant.USER_CAPTCHA_KEY, captChaReqVO.getMobile());
        RedisUtil.setCacheObject(key, verCode);
        RedisUtil.expire(key, 60);

        return base64;
    }

    @Override
    public String sendVerificationCode(SendMobileSmsCodeReqVO smsCodeReqVO) {
        String verCode = VER_CODE;
        if (!TEST_ENVIRONMENT.contains(application)) {
            verCode = RandomUtil.randomNumbers(6);
        }

        String key = StrUtil.format(UserConstant.USER_LOGIN_KEY_PREFIX, smsCodeReqVO.getMobile());
        RedisUtil.setCacheObject(key, verCode);
        RedisUtil.expire(key, 60*5);

        return "发送成功";
    }

    @Override
    public LoginRespVO mobileLogin(LoginMobileReqVO mobileReqVO) {
        String key = StrUtil.format(UserConstant.USER_LOGIN_KEY_PREFIX, mobileReqVO.getMobile());
        AssertUtils.isTrue(RedisUtil.hasKey(key), "手机验证码错误");

        String sms = RedisUtil.getCacheObject(key);

        AssertUtils.isTrue(mobileReqVO.getVerificationCode().equals(sms), "手机验证码错误");
        RedisUtil.deleteObject(key);

        SysAccount account = accountMapper.selectOne(Wrappers.<SysAccount>lambdaQuery()
                .eq(SysAccount::getMobile, mobileReqVO.getMobile())
                .eq(SysAccount::getDeleted, Boolean.FALSE)
                .select(SysAccount::getId, SysAccount::getUserType, SysAccount::getNodeLabel, SysAccount::getMainTenantCode, SysAccount::getFirstLogin)
        );

        AssertUtils.isTrue(Objects.nonNull(account) && Objects.nonNull(account.getId()), "手机号未注册,请联系企业管理员");

        return getLoginRespVO(account);
    }

    @Override
    public LoginRespVO accountLogin(LoginAccountReqVO accountReqVO) {
        SysUser sysUser = null;
        if (StrUtil.isBlank(accountReqVO.getMobile())) {
            Long count = sysUserMapper.selectCount(Wrappers.<SysUser>lambdaQuery()
                    .eq(SysUser::getDeleted, Boolean.FALSE)
                    .eq(SysUser::getAccount, accountReqVO.getAccount())
            );
            AssertUtils.isTrue(count > 0, "账号或密码不正确");
            AssertUtils.isTrue(count == 1, ExceptionCodeEnum.USER_ACCOUNT_LOGIN_NEED_MOBILE);

            sysUser = sysUserMapper.selectOne(Wrappers.<SysUser>lambdaQuery()
                    .eq(SysUser::getDeleted, Boolean.FALSE)
                    .eq(SysUser::getAccount, accountReqVO.getAccount())
                    .select(SysUser::getId, SysUser::getNickName, SysUser::getMobile, SysUser::getMail, SysUser::getDeptId, SysUser::getTenancyCode)
            );
        }

        SysAccount account = accountMapper.selectOne(Wrappers.<SysAccount>lambdaQuery()
                .eq(SysAccount::getMobile, Optional.ofNullable(sysUser).map(SysUser::getMobile).orElse(accountReqVO.getMobile()))
                .eq(SysAccount::getDeleted, Boolean.FALSE)
                .select(SysAccount::getId, SysAccount::getUserType, SysAccount::getNodeLabel, SysAccount::getPassword, SysAccount::getMainTenantCode, SysAccount::getFirstLogin)
        );

        String password = SecureUtil.sha256(accountReqVO.getPassword());
        AssertUtils.isTrue(Objects.nonNull(account) && password.equals(account.getPassword()), "账号或密码不正确");

        return getLoginRespVO(account);
    }



    @Override
    public String loginOut() {
        String token = UaaAuthHelper.getToken();
        AssertUtils.isTrue(StrUtil.isNotBlank(token), AnswerCodeEnum.NO_AUTH);

        GateWayLoginOutReqVo tokenReqVo = new GateWayLoginOutReqVo();
        tokenReqVo.setAccountId(null);
        tokenReqVo.setToken(token);
        tokenReqVo.setIsConcurrent(true);
        tokenReqVo.setClientType(UaaAuthHelper.getClientType());
        tokenReqVo.setClientId(chinaisafeAuthorizationClientProperties.getClientId());
        tokenReqVo.setClientSecret(chinaisafeAuthorizationClientProperties.getClientSecret());

        Answer<Boolean> loginOut = gateWayUserClient.loginOut(tokenReqVo);
        log.info("退出成功:{}", loginOut);
        return "退出成功";
    }

    @Override
    public LoginRespVO loginTenant(LoginTenantReqVO tenantReqVO) {
        AuthUser user = UaaAuthHelper.getUser();

        SysAccount account = accountMapper.selectOne(Wrappers.<SysAccount>lambdaQuery()
                .eq(SysAccount::getId, user.getAccountId())
                .eq(SysAccount::getDeleted, Boolean.FALSE)
                .select(SysAccount::getId, SysAccount::getUserType, SysAccount::getFirstLogin, SysAccount::getNodeLabel)
        );
        // 从请求头里面拿到客户端类型
        ClientTypeEnum clientType = UaaAuthHelper.getClientType();

        GatewayChangeTokenReqVo tokenReqVo = new GatewayChangeTokenReqVo();
        tokenReqVo.setToken(UaaAuthHelper.getToken());
        tokenReqVo.setClientType(clientType);
        // 秘钥
        tokenReqVo.setClientId(chinaisafeAuthorizationClientProperties.getClientId());
        tokenReqVo.setClientSecret(chinaisafeAuthorizationClientProperties.getClientSecret());

        // 缓存用户信息
        AuthUser authUser = new AuthUser();

        SysUser sysUser = sysUserMapper.selectOne(Wrappers.<SysUser>lambdaQuery()
                .eq(SysUser::getDeleted, Boolean.FALSE)
                .eq(SysUser::getSysAccountId, user.getAccountId())
                .eq(SysUser::getTenancyCode, tenantReqVO.getTenantCode())
                .select(SysUser::getId, SysUser::getUserType, SysUser::getNickName, SysUser::getMobile, SysUser::getMail, SysUser::getDeptId, SysUser::getTenancyCode)
        );
        UserTypeEnum userType = UserTypeEnum.getByCode(account.getUserType());
        authUser.setUserType(userType);
        authUser.setHasAdmin(false);
        if (UserTypeEnum.ADMIN == userType) {
            authUser.setNickName(UserTypeEnum.ADMIN.getMessage());
            authUser.setHasAdmin(true);
        }
        authUser.setAccountId(user.getAccountId());
        authUser.setIp(UaaAuthHelper.getIp());
        authUser.setNodeLabel(account.getNodeLabel());
        authUser.setAccountId(user.getAccountId());
        authUser.setUserId(sysUser.getId());
        authUser.setNickName(sysUser.getNickName());
        authUser.setMobile(sysUser.getMobile());
        authUser.setMail(sysUser.getMail());
        authUser.setDeptId(sysUser.getDeptId());
        authUser.setTenancyCode(tenantReqVO.getTenantCode());

        SysTenant tenant = sysTenantMapper.selectOne(Wrappers.<SysTenant>lambdaQuery()
                .eq(SysTenant::getCode, tenantReqVO.getTenantCode()).select(SysTenant::getCode, SysTenant::getName).last("limit 1"));
        if (Objects.nonNull(tenant)) {
            authUser.setTenancyName(tenant.getName());
        }

        authUser.setDeptId(sysUser.getDeptId());
        SysDept dept = sysDeptMapper.selectOne(Wrappers.<SysDept>lambdaQuery().eq(SysDept::getId, sysUser.getDeptId())
                .select(SysDept::getCode, SysDept::getName).last("limit 1"));
        if (Objects.nonNull(dept)) {
            authUser.setDeptCode(dept.getCode());
            authUser.setDeptName(dept.getName());
        }

        tokenReqVo.setUser(authUser);

        Answer<String> changedToken = gateWayUserClient.changeToken(tokenReqVo);

        // 登录响应值
        LoginRespVO loginRespVo = new LoginRespVO();
        loginRespVo.setToken(changedToken.getData());
        loginRespVo.setUserType(userType);
        loginRespVo.setFirstLogin(account.getFirstLogin());

        return loginRespVo;
    }

    @Override
    public UserInfoRespVO getUserInfo() {
        AuthUser user = UaaAuthHelper.getUser();
        SysUser sysUser = sysUserMapper.selectOne(Wrappers.<SysUser>lambdaQuery()
                .eq(SysUser::getDeleted, Boolean.FALSE)
                .eq(SysUser::getId, user.getUserId())
                .select(SysUser::getId, SysUser::getNickName, SysUser::getAvatar, SysUser::getSignature, SysUser::getDeptId, SysUser::getTenancyCode, SysUser::getUserType)
        );

        UserInfoRespVO userInfoRespVo = new UserInfoRespVO();
        userInfoRespVo.setUserId(sysUser.getId());
        userInfoRespVo.setNickName(sysUser.getNickName());
        userInfoRespVo.setAvatar(sysUser.getAvatar());
        userInfoRespVo.setDeptId(sysUser.getDeptId());
        userInfoRespVo.setSignature(sysUser.getSignature());
        userInfoRespVo.setUserType(sysUser.getUserType());
        userInfoRespVo.setTenantCode(sysUser.getTenancyCode());

        userInfoRespVo.setTenantName(user.getTenancyName());

        return userInfoRespVo;
    }

    /**
     * 获取登录响应值
     * @param account
     * @return
     * @author xuefu.shu
     * @date 2024-07-13 13:39
     */
    private LoginRespVO getLoginRespVO(SysAccount account) {
        // 从请求头里面拿到客户端类型
        ClientTypeEnum clientType = UaaAuthHelper.getClientType();

        GateWayGetTokenReqVo tokenReqVo = new GateWayGetTokenReqVo();
        tokenReqVo.setAccountId(account.getId());
        tokenReqVo.setClientType(clientType);
        // 秘钥
        tokenReqVo.setClientId(chinaisafeAuthorizationClientProperties.getClientId());
        tokenReqVo.setClientSecret(chinaisafeAuthorizationClientProperties.getClientSecret());

        // 缓存用户信息
        AuthUser authUser = new AuthUser();
        UserTypeEnum userType = UserTypeEnum.getByCode(account.getUserType());
        authUser.setUserType(userType);
        authUser.setMobile(account.getMobile());
        authUser.setHasAdmin(false);
        if (UserTypeEnum.ADMIN == userType) {
            authUser.setNickName(UserTypeEnum.ADMIN.getMessage());
            authUser.setHasAdmin(true);
        }

        SysUser sysUser = sysUserMapper.selectOne(Wrappers.<SysUser>lambdaQuery()
                .eq(SysUser::getDeleted, Boolean.FALSE)
                .eq(SysUser::getSysAccountId, account.getId())
                .eq(SysUser::getTenancyCode, account.getMainTenantCode())
                .select(SysUser::getId, SysUser::getNickName, SysUser::getMobile, SysUser::getMail, SysUser::getDeptId, SysUser::getTenancyCode)
        );

        authUser.setAccountId(account.getId());
        authUser.setNodeLabel(account.getNodeLabel());
        if (Objects.nonNull(sysUser)) {
            authUser.setUserId(sysUser.getId());
            authUser.setNickName(sysUser.getNickName());
            authUser.setMobile(sysUser.getMobile());
            authUser.setMail(sysUser.getMail());
            authUser.setDeptId(sysUser.getDeptId());
            authUser.setTenancyCode(sysUser.getTenancyCode());
        }
        authUser.setIp(UaaAuthHelper.getIp());

        tokenReqVo.setCacheUser(authUser);

        Answer<String> token = gateWayUserClient.getToken(tokenReqVo);

        AssertUtils.isTrue(Objects.nonNull(token) && StrUtil.isNotBlank(token.getData()), "网络异常，登录失败");

        // 登录响应值
        LoginRespVO loginRespVO = new LoginRespVO();
        loginRespVO.setToken(token.getData());
        loginRespVO.setFirstLogin(account.getFirstLogin());
        loginRespVO.setUserType(userType);

        return loginRespVO;
    }

    @Override
    public Answer<AuthUser> getUserInfo(Long userId) {
        SysUser sysUser = sysUserMapper.selectOne(Wrappers.<SysUser>lambdaQuery()
                .eq(SysUser::getDeleted, Boolean.FALSE)
                .eq(SysUser::getId, userId)
                .select(SysUser::getId, SysUser::getNickName, SysUser::getMobile, SysUser::getMail, SysUser::getDeptId, SysUser::getTenancyCode)
        );

        if (Objects.nonNull(sysUser)) {
            return Answer.fail();
        }

        SysTenant sysTenant = null;
        if (Objects.nonNull(sysUser.getTenancyCode())) {
            sysTenant = sysTenantMapper.selectOne(Wrappers.<SysTenant>lambdaQuery()
                    .eq(SysTenant::getCode, sysUser.getTenancyCode())
                    .eq(SysTenant::getEnable, Boolean.TRUE).select(SysTenant::getCode, SysTenant::getName));
        }

        SysDept dept = null;
        if (Objects.nonNull(sysUser.getDeptId())) {
            dept = sysDeptMapper.selectOne(Wrappers.<SysDept>lambdaQuery().eq(SysDept::getId, sysUser.getAdministrationDeptId())
                    .eq(SysDept::getEnable, Boolean.TRUE).select(SysDept::getId, SysDept::getCode, SysDept::getName));
        }

        AuthUser user = AuthUser.builder()
                .userId(userId)
                .nickName(sysUser.getNickName())
                .deptId(sysUser.getDeptId())
                .tenancyCode(sysUser.getTenancyCode())
                .deptCode(Optional.ofNullable(dept).map(SysDept::getCode).filter(StrUtil::isNotBlank).orElse(""))
                .deptName(Optional.ofNullable(dept).map(SysDept::getName).filter(StrUtil::isNotBlank).orElse(""))
                .tenancyName(Optional.ofNullable(sysTenant).map(SysTenant::getCode).orElse(""))
                .build();

        return Answer.successWithData(user);
    }

    @Override
    public Answer<Boolean> getAuthorities(UserCheckSourceReqVo checkSourceReqVo) {


        return Answer.successWithData(true);
    }
}
